package com.csaba.swing.gui.wizard;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSeparator;

import com.csaba.swing.MainWindow;
import com.csaba.swing.gui.GUIContext;
import com.csaba.swing.gui.login.LoginWizardModel;
import com.csaba.util.LoggerHelper;

//FIXME doc
@SuppressWarnings( "serial" )
public class WizardDialog extends JDialog implements ActionListener
{
	private final static Logger logger = LoggerHelper.getDefaultLogger();

	//private final JXHeader header;
	private final WizardHeader header;

	private final JButton next;
	private final JButton back;
	private final JButton finish;
	private final JButton cancel;

	private final JPanel content;

	private WizardModel wizardModel;

	private final WizardContext context;

	private int currentPageIndex = -1;

	private boolean ready = false;

	private final JFrame frame;

	private final Map<WizardPage, String> pagesAdded = new HashMap<WizardPage, String>();

	public WizardDialog( final JFrame frame, final String title, final String description, final ImageIcon icon,
			final WizardModel wizardModel )
	{
		super(frame, title);
		this.wizardModel = wizardModel;
		this.frame = frame;

		header = new WizardHeader(title, description);
		header.setIcon(icon);

		next = new JButton(MainWindow.getString("WizardDialog.next")); //$NON-NLS-1$
		back = new JButton(MainWindow.getString("WizardDialog.back")); //$NON-NLS-1$
		finish = new JButton(MainWindow.getString("WizardDialog.finish")); //$NON-NLS-1$
		cancel = new JButton(MainWindow.getString("WizardDialog.cancel")); //$NON-NLS-1$
		cancel.addActionListener(this);
		finish.addActionListener(this);
		next.addActionListener(this);
		back.addActionListener(this);

		final JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
		buttonPanel.add(back);
		buttonPanel.add(next);
		buttonPanel.add(new JSeparator(JSeparator.VERTICAL));
		buttonPanel.add(finish);
		buttonPanel.add(cancel);

		content = new JPanel(new CardLayout(5, 5));

		final Container root = getContentPane();
		root.setLayout(new BorderLayout());

		root.add(header, BorderLayout.NORTH);
		root.add(content, BorderLayout.CENTER);
		root.add(buttonPanel, BorderLayout.SOUTH);

		context = new WizardContext(this);
		loadPage(0, WizardPage.ACTION_NEXT);

		setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
		setModal(true);
		pack();
		setLocationRelativeTo(frame);
	}

	public void backPressed()
	{
		if ( back.isEnabled() )
			loadPage(currentPageIndex - 1, WizardPage.ACTION_BACK);

	}

	public void nextPressed()
	{
		if ( next.isEnabled() )
			loadPage(currentPageIndex + 1, WizardPage.ACTION_NEXT);

	}

	public void finishPressed()
	{
		if ( finish.isEnabled() )
		{
			if ( !leaveCurrentPage(WizardPage.ACTION_FINISH) )
			{
				return;
			}

			dispose();
		}
	}

	public void cancelPressed()
	{
		if ( cancel.isEnabled() )
		{
			if ( !leaveCurrentPage(WizardPage.ACTION_CANCEL) )
			{
				return;
			}

			dispose();
		}
	}

	public void setDescription( final String description )
	{
		header.setDescription(description);
		pack();
		setLocationRelativeTo(frame);
	}

	public void setIcon( final ImageIcon icon )
	{
		header.setIcon(icon);
		pack();
		setLocationRelativeTo(frame);
	}

	public void setWarning( final String string )
	{
		header.setMessage(MainWindow.getString("WizardDialog.msg.warning", string)); //$NON-NLS-1$

	}

	public String getMessage()
	{
		return header.getMessage();
	}

	public void clearMessage()
	{
		header.clearMessage();

	}

	public void setWizardModel( final WizardModel wizardHandler )
	{
		this.wizardModel = wizardHandler;
	}

	public WizardModel getWizardModel()
	{
		return wizardModel;
	}

	public WizardContext getContext()
	{
		return context;
	}

	private void updateButtonState()
	{
		//back / next is visible only if more than 1 page exists in the model
		final int pageCount = wizardModel.getPageCount();
		next.setVisible(pageCount > 1);
		back.setVisible(pageCount > 1);

		//set next/back button state
		next.setEnabled(currentPageIndex < pageCount - 1);
		back.setEnabled(currentPageIndex > 0);

		//finish should be enabled only if ready flag is true
		finish.setEnabled(ready);
	}

	private boolean leaveCurrentPage( final int action )
	{
		if ( currentPageIndex >= 0 )
		{
			final WizardPage currentPage = wizardModel.getPageAt(currentPageIndex);
			if ( !currentPage.leave(action) )
			{
				return false;
			}
		}
		clearMessage();
		return true;
	}

	private void loadPage( final int index, final int action )
	{
		if ( index < 0 || index >= wizardModel.getPageCount() )
			return;

		if ( !leaveCurrentPage(action) )
			return;

		currentPageIndex = index;
		final WizardPage currentPage = wizardModel.getPageAt(currentPageIndex);
		if ( pagesAdded.containsKey(currentPage) )
		{
			final String name = pagesAdded.get(currentPage);
			( (CardLayout) content.getLayout() ).show(content, name);
			logger.fine("Show existing wizard page: " + name); //$NON-NLS-1$
		}
		else
		{
			currentPage.init(context);

			String name = currentPage.getClass().getName();
			while ( pagesAdded.containsValue(name) )
			{
				name += "_"; //$NON-NLS-1$
			}

			logger.fine("Registering new wizard page: " + name); //$NON-NLS-1$
			pagesAdded.put(currentPage, name);
			content.add(currentPage, name);

			( (CardLayout) content.getLayout() ).show(content, name);

			pack();
			setLocationRelativeTo(frame);
		}

		updateButtonState();

		currentPage.updateView();
	}

	@Override
	public void actionPerformed( final ActionEvent event )
	{
		if ( event.getSource() == next )
		{
			nextPressed();
		}
		else if ( event.getSource() == back )
		{
			backPressed();
		}
		else if ( event.getSource() == finish )
		{
			finishPressed();
		}
		else if ( event.getSource() == cancel )
		{
			cancelPressed();
		}

	}

	public static void main( final String[] args ) throws InterruptedException
	{
		LoggerHelper.initLogger(Level.FINEST);
		final JFrame frame = new JFrame();
		frame.setSize(500, 300);
		frame.setVisible(true);

		Thread.sleep(100);
		final WizardDialog dialog = new WizardDialog(frame, "Test Wizard", "", null, new LoginWizardModel()); //$NON-NLS-1$ //$NON-NLS-2$
		dialog
				.setDescription("This is a test dialog box, where we have a very nice header area, a pagaeable" //$NON-NLS-1$
						+ " content area, and the button area with the default navigation buttons: next, back, finish, cancel. " //$NON-NLS-1$
						+ "In this test I provide a long description and test icons."); //$NON-NLS-1$
		dialog.setIcon(GUIContext.getIcon("login", 32)); //$NON-NLS-1$
		dialog.setVisible(true);
		frame.dispose();
	}

	public void setReady( final boolean ready )
	{
		this.ready = ready;
		updateButtonState();
	}

	public boolean isReady()
	{
		return ready;
	}

	public WizardPage getCurrentPage()
	{
		return wizardModel.getPageAt(currentPageIndex);
	}

}
